package com.anlogic.sdk.bsp.view.ui;

import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;

import com.anlogic.sdk.app.handlers.ApplicationProjectCreator;
import com.anlogic.sdk.app.model.ApplicationModel;
import com.anlogic.sdk.bsp.model.IPlatformProject;
import com.anlogic.sdk.bsp.utils.PlatformUtils;
import com.anlogic.sdk.tools.IBspTool;
import com.anlogic.sdk.tools.utils.ToolsUtils;
import com.anlogic.sdk.utils.EnvUtils;
import com.anlogic.sdk.utils.FileUtils;
import com.anlogic.sdk.utils.StringUtils;
import com.anlogic.sdk.utils.options.BitWidth;
import com.anlogic.sdk.utils.options.ChipType;
import com.anlogic.sdk.utils.options.LanguageType;
import com.anlogic.sdk.utils.options.OsType;

public class DriverExampleDialog extends TitleAreaDialog {
	private Text projectNameText;
	private Button useDefaultLocButton;
	private Text locationText;
	private Button locationBrowse;
	private Button cLanguageButton;
	private Button cppLanguageButton;
	private Button managedBuildOn;
	private Combo bitWidthCombo;
	private List appList;
	
	private String driverName;
	
	private IBspTool bspTool;
	
	private ApplicationModel model;
	
	private String extraLibs;

	public DriverExampleDialog(Shell parentShell, IPlatformProject platformProject, String driverName) {
		super(parentShell);
		setShellStyle(getShellStyle() | SWT.RESIZE);
		this.driverName = driverName;
		this.bspTool = ToolsUtils.getBspTool();
		this.bspTool.setChip(platformProject.getChip());
		this.model = new ApplicationModel();
		this.model.setPlatform(platformProject);
		this.model.setLang(LanguageType.C.getName());
	}

	private void enableOkButton(boolean flag) {
		Button button = getButton(0);
		if (button != null)
			button.setEnabled(flag);
	}
	
	private void renameOKButton() {
		Button okButton = getButton(0);
		if (okButton != null) {
			okButton.setText("Create");
			setButtonLayoutData(okButton);
		}
	}
	
	@Override
	protected Control createButtonBar(Composite parent) {
		Control c = super.createButtonBar(parent);
		renameOKButton();
		validatePage();
		return c;
	}
	
	@Override
	protected Control createDialogArea(Composite parent) {
		setTitle("Import Example");
		setMessage("Select the examples to be imported into workpsace");

		Composite container = new Composite(parent, SWT.DEFER);
		container.setLayout(new GridLayout(3, false));
		container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		Label projectNameLabel = new Label(container, SWT.LEFT);
		projectNameLabel.setText("Project Name:");
		projectNameText = new Text(container, SWT.BORDER | SWT.SINGLE);
		projectNameText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
		
		useDefaultLocButton = new Button(container, SWT.CHECK);
		useDefaultLocButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1));
		useDefaultLocButton.setText("Use default Location");
		useDefaultLocButton.setSelection(true);

		Label locationLabel = new Label(container, SWT.LEFT);
		locationLabel.setText("Location:");
		locationText = new Text(container, SWT.BORDER | SWT.SINGLE);
		locationText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
		locationText.setText(EnvUtils.getWorkspacePath());
		locationText.setEnabled(false);
		locationBrowse = new Button(container, SWT.PUSH);
		locationBrowse.setText("Browse");
		locationBrowse.setEnabled(false);
		this.model.setProjLoc(locationText.getText());
		
		managedBuildOn = new Button(container, SWT.CHECK);
		managedBuildOn.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1));
		managedBuildOn.setText("ManagedBuildOn");
		managedBuildOn.setSelection(false);
		
		Label languageLabel = new Label(container, SWT.LEFT);
		languageLabel.setText("Language:");
		Composite languageComp = new Composite(container, SWT.NULL);
		GridDataFactory.swtDefaults().span(2, 1).applyTo((Control) languageComp);
		GridLayoutFactory.fillDefaults().numColumns(2).applyTo(languageComp);
		cLanguageButton = new Button(languageComp, SWT.RADIO);
		cLanguageButton.setText(LanguageType.C.getName());
		cLanguageButton.setSelection(true);
		this.model.setLang(LanguageType.C.getName());
		cppLanguageButton = new Button(languageComp, SWT.RADIO);
		cppLanguageButton.setText(LanguageType.CPP.getName());
		
		Label bitWidthLabel = new Label(container, SWT.LEFT);
		bitWidthLabel.setText("Bit Width:");
		bitWidthCombo = new Combo(container, SWT.POP_UP);
		GridDataFactory.swtDefaults().span(2, 1).applyTo((Control) bitWidthCombo);
		GridLayoutFactory.fillDefaults().numColumns(2).applyTo(bitWidthCombo);
		
		String[] bitWidthList = getBitWidthList(this.model.getBspName());
		bitWidthCombo.setItems(bitWidthList);
		bitWidthCombo.select(0);
		this.model.setBitWidth(bitWidthCombo.getItem(bitWidthCombo.getSelectionIndex()));
		
		String chip = this.model.getChip();
		if (ChipType.isAArch64(chip)) {
			this.bitWidthCombo.setEnabled(true);
		} else {
			this.bitWidthCombo.setEnabled(false);
		}
		
		Label appNameLabel = new Label(container, SWT.LEFT);
		appNameLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1));
		appNameLabel.setText("Template List:");
		appList = new List(container, SWT.SIMPLE);
		appList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
		
		updateApps();
		
		addListeners();
		
		validatePage();
		
		return (Control) parent;
	}
	
	private void updateApps() {
		String bitWidth = this.model.getBitWidth();
		String osType = this.model.getOs();
		if (bitWidth.equals(BitWidth.BIT32.getName()) && !osType.equals(OsType.Standalone.getName())) {
			this.appList.setItems(new String[0]);
			return;
		}
		java.util.List<String> appNames = this.bspTool.getDriverAppNames(this.driverName, this.model.getOs().toLowerCase(), this.model.getChip(), this.model.getProc());
		this.appList.setItems(appNames.toArray(new String[0]));
		this.appList.select(0);
		appModified();
	}
	
	private void addListeners() {
		projectNameText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				validatePage();
			}
		});
		
		locationText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				validatePage();
			}
		});
		
		useDefaultLocButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				locationText.setEnabled(!useDefaultLocButton.getSelection());
				locationBrowse.setEnabled(!useDefaultLocButton.getSelection());
				validatePage();
			}
		});

		locationBrowse.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				DirectoryDialog dialog = new DirectoryDialog(getShell(), SWT.OPEN);
				String result = dialog.open();
				if (result != null && !result.isEmpty()) {
					locationText.setText(result);
				}
			}
		});
		
		cLanguageButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				model.setLang(LanguageType.C.getName());
				validatePage();
			}
		});
		
		cppLanguageButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				model.setLang(LanguageType.CPP.getName());
				validatePage();
			}
		});
		
		managedBuildOn.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				model.setManagedBuildOn(managedBuildOn.getSelection());
				validatePage();
			}
		});
		
		bitWidthCombo.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				model.setBitWidth(bitWidthCombo.getItem(bitWidthCombo.getSelectionIndex()));
				updateApps();
				validatePage();
			}
		});
		
		appList.addSelectionListener((SelectionListener) new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				appModified();
				validatePage();
			}
		});
	}
	
	private void appModified() {
		int index = this.appList.getSelectionIndex();
		if (index != -1) {
			String appName = this.appList.getItem(index);
			this.model.setAppName(appName);
		} else {
			this.model.setAppName("");
		}
		
		java.util.List<String> extraLibList = this.bspTool.getAppExtraLibs(this.model.getPlatform().getMssPath(), this.model.getAppName());
		if (!extraLibList.isEmpty()) {
			this.extraLibs = StringUtils.getCSVString(extraLibList);
		} else {
			this.extraLibs = "";
		}
	}
	
	private void validatePage() {
		if (!validateProject()) {
			enableOkButton(false);
			return;
		}
		
		if (!validateLocation()) {
			enableOkButton(false);
			return;
		}
		
		if (!validateApp()) {
			enableOkButton(false);
			return;
		}
		
		setMessage(null);
		setErrorMessage(null);
		
		enableOkButton(true);
	}
	
	private boolean validateProject() {
		String projName = this.projectNameText.getText();
		if (projName == null || projName.length() == 0) {
			setErrorMessage("Project Name must be specified");
			return false;
		}
		if (!FileUtils.checkFileNameLegality(projName)) {
			setErrorMessage("Project name is illegal");
			return false;
		}
		IResource projectSource = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(projName));
		if (projectSource != null) {
			setErrorMessage("Project has already exist!");
			return false;
		}
		this.model.setProjName(projName);
		return true;
	}
	
	private boolean validateLocation() {
		String projLoc = locationText.getText();
		if (projLoc == null || projLoc.length() == 0) {
			setErrorMessage("Project Name must be specified");
			return false;
		}
		if (!FileUtils.checkFilePathLegality(projLoc)) {
			setErrorMessage("Project location is illegal");
			return false;
		}
		projLoc = FileUtils.convertToLinuxPath(projLoc);
		IPath projLocPath = Path.fromPortableString(projLoc).append(this.projectNameText.getText());
		this.model.setProjLoc(projLocPath.toString());
		return true;
	}
	
	private boolean validateApp() {
		if (this.extraLibs != null && !this.extraLibs.isEmpty()) {
			setErrorMessage(String.format("This application requires %s library or driver in the Platform Project.", this.extraLibs));
			return false;
		}
		return true;
	}
	
	@Override
	protected void okPressed() {
		super.okPressed();
		IProgressService iProgressService = PlatformUI.getWorkbench().getProgressService();
		try {
			iProgressService.run(true, true, new IRunnableWithProgress() {
				@Override
				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
					ApplicationProjectCreator creator = new ApplicationProjectCreator();
					creator.createApplicationProject(model, monitor);
				}
			});
		} catch (InvocationTargetException e) {
			String message = "Create App encountered an issue. Some of the actions might not work as expected.";
			MessageDialog.openError(null, "Create App Error", message);
		} catch (InterruptedException e) {
			String message = "Create App has been cancelled. Some of the actions might not work as expected.";
			MessageDialog.openWarning(null, "Create App Cancelled", message);
		}
	}
	
	private String[] getBitWidthList(String bspName) {
		IPlatformProject platformProject = PlatformUtils.getPlatformProject(bspName);
		String os = platformProject.getOs();
		if (OsType.isStandalone(os)) {
			return BitWidth.getNames();
		} else {
			String[] bitList = {BitWidth.BIT64.getName()};
			return bitList;
		}
	}
}
